package org.ithasu.uBroadcast.connectors;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;

import org.ithasu.uBroadcast.UMessage;

public class UDPConnector {

	public static final int DEFAULT_PORT = 54545;

	private byte id[];
	private int port;
	private DatagramSocket udpSocket;

	public UDPConnector(byte[] id, int port, int timeout_ms)
			throws SocketException {
		this.id = id;
		this.port = port;
		udpSocket = new DatagramSocket(null);
		udpSocket.setBroadcast(true);
		udpSocket.setReuseAddress(true);
		udpSocket.setSoTimeout(timeout_ms);

		InetSocketAddress recvAddr = new InetSocketAddress((InetAddress) null,
				this.port);
		udpSocket.bind(recvAddr);
	}

	@Override
	protected void finalize() throws Throwable {
		super.finalize();
		if (udpSocket != null) {
			udpSocket.close();
			udpSocket = null;
		}
	}

	// --- Send message ----------------------------------------

	public boolean send(UMessage msg) {
		try {
			if (udpSocket == null || msg == null) {
				return false;
			}
			// Prepare data
			byte[] buffer = msg.toByteArray();
			byte[] data = new byte[buffer.length + 6];

			System.arraycopy(id, 0, data, 0, id.length);
			System.arraycopy(buffer, 0, data, id.length, buffer.length);

			InetSocketAddress toAddr = new InetSocketAddress(
					getBroadcastAddress(), port);
			// Create and send packet
			DatagramPacket packet = new DatagramPacket(data, data.length,
					toAddr);
			udpSocket.send(packet);
			return true;
		} catch (Exception e) {
			System.err.println(e);
			return false;
		}
	}

	// --- Receive message ----------------------------------------

	public UMessage recv() throws IOException {
		byte[] tmp = new byte[UMessage.UB_MESSAGE_SIZE + 6];
		DatagramPacket packet = new DatagramPacket(tmp, tmp.length);
		udpSocket.receive(packet);

		byte[] tmp_id = Arrays.copyOfRange(tmp, 0, 6);
		if (Arrays.equals(tmp_id, id)) {
			return null;
		}
		byte[] data = Arrays.copyOfRange(tmp, 6, tmp.length);
		UMessage message = new UMessage(data);
		if (message.isValid()) {
			return message;
		} else {
			return null;
		}
	}

	// --- Utils ----------------------------------------

	private final static byte[] BROADCAST_ADDRESS = new byte[] { (byte) 0xff,
			(byte) 0xff, (byte) 0xff, (byte) 0xff };
	private static InetAddress broadcastAddr = null;

	private final static InetAddress getBroadcastAddress()
			throws UnknownHostException {
		// Lazy broadcast address init
		if (broadcastAddr == null) {
			broadcastAddr = InetAddress.getByAddress(BROADCAST_ADDRESS);
		}
		return broadcastAddr;

	}
}
